﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace VehicleTrajectoryApp
{
    class CSVParser
    {
        private char Delimiter;
        private bool IsFirstLineHeader;
        private StreamReader reader;
        private List<string> Headers;
        private List<string> LineFieldValue;
        private Dictionary<string, int> FieldIndexer;

        private List<string> ParseLine(string line)
        {
            if (line != null)
            {
                line.Trim();
                List<string> SeperatedStrings = new List<string>();

                // if do not include double quotation mark
                if (line.IndexOf('"') == -1)
                {
                    string[] subString = line.Split(Delimiter);
                    SeperatedStrings = new List<string>(subString);

                }
                else
                {
                    while (line.Length > 0)
                    {
                        string subStr;
                        int n1 = line.IndexOf(',');
                        int n2 = line.IndexOf('"');

                        if (n1 == -1 && n2 == -1) //last field without double quotes
                        {
                            subStr = line;
                            SeperatedStrings.Add(subStr);
                            break;
                        }

                        if (n1 == -1 && n2 != -1) //last field with double quotes
                        {
                            int n3 = line.IndexOf('"', n2 + 1); // second double quote

                            //extract content from double quotes
                            subStr = line.Substring(n2 + 1, n3 - n2 - 1);
                            SeperatedStrings.Add(subStr);

                            break;
                        }

                        if (n1 != -1 && (n1 < n2 || n2 == -1))
                        {
                            subStr = line.Substring(0, n1);
                            SeperatedStrings.Add(subStr);
                            if (n1 < line.Length - 1)
                            {
                                line = line.Substring(n1 + 1);
                            }
                            else // comma is the last char in the line string, push an empty string to the back of vector
                            {
                                SeperatedStrings.Add("");
                                break;
                            }
                        }

                        if (n1 != -1 && n2 != -1 && n2 < n1)
                        {
                            int n3 = line.IndexOf('"', n2 + 1); // second double quote
                            subStr = line.Substring(n2 + 1, n3 - n2 - 1);
                            SeperatedStrings.Add(subStr);
                            int idx = line.IndexOf(',', n3 + 1);

                            if (idx != -1)
                            {
                                line = line.Substring(idx + 1);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                }

                return SeperatedStrings;
            }
            else
            {
                return null;
            }
        }

        public CSVParser()
        {
            Delimiter = ',';
            IsFirstLineHeader = true;
            Headers = new List<string>();
            LineFieldValue = new List<string>();
            FieldIndexer = new Dictionary<string, int>();
        }

        public bool ReadRecord()
        {
            try
            {
                string s;
                s = reader.ReadLine();
                if (s != null && s.Length > 0)
                {
                    LineFieldValue = ParseLine(s);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (IOException e)
            {
                System.Windows.Forms.MessageBox.Show(String.Format("Unable to reader CSV File.I/O exception: {0} is received!", e.ToString()));
                reader.Close();
                return false;
            }
        }

        public bool OpenCSVFile(string csvFileName)
        {
            try
            {
                if (reader != null)
                {
                    reader.Close();
                    reader = null;
                }

                reader = new StreamReader(csvFileName);

                if (IsFirstLineHeader)
                {
                    string s = reader.ReadLine();
                    Headers = ParseLine(s);

                    for (int n = 0; n < Headers.Count; n++)
                    {
                        FieldIndexer.Add(Headers[n].Trim(), n);
                    }
                }
                return true;
            }
            catch (IOException e)
            {
                System.Windows.Forms.MessageBox.Show(String.Format("Unable to reader CSV File.I/O exception: {0} is received!", e.ToString()));
                reader.Close();
                return false;
            }
        }

        public void CloseCSVFile()
        {
            reader.Close();
        }

        private bool GetStringValueByFieldName(string field_name, out string valueInString)
        {
            if (field_name != null)
            {
                if (FieldIndexer.ContainsKey(field_name))
                {
                    int index = FieldIndexer[field_name];
                    valueInString = LineFieldValue[index];
                    return true;
                }
                else
                {
                    valueInString = null;
                    return false;
                }
            }
            else
            {
                valueInString = null;
                return false;
            }
        }

        public bool GetValueByFieldName(string field_name,out int value)
        {
            string valueInString;
            if (GetStringValueByFieldName(field_name, out valueInString))
            {
                valueInString.Trim();
                if (valueInString.Length > 0)
                {
                    try
                    {
                        value = Int32.Parse(valueInString);
                        return true;
                    }
                    catch (Exception)
                    {
                        value = 0;
                        return false;
                    }
                }
                else
                {
                    value = 0;
                    return false;
                }
            }
            else
            {
                value = 0;
                return false;
            }
        }

        public bool GetValueByFieldName(string field_name, out float value)
        {
            string valueInString;
            if (GetStringValueByFieldName(field_name, out valueInString))
            {
                valueInString.Trim();
                if (valueInString.Length > 0)
                {
                    try
                    {
                        value = float.Parse(valueInString);
                        return true;
                    }
                    catch (Exception)
                    {
                        value = 0.0f;
                        return false;
                    }
                }
                else
                {
                    value = 0.0f;
                    return false;
                }
            }
            else
            {
                value = 0.0f;
                return false;
            }
        }

        public bool GetValueByFieldName(string field_name, out double value)
        {
            string valueInString;
            if (GetStringValueByFieldName(field_name, out valueInString))
            {
                valueInString.Trim();
                if (valueInString.Length > 0)
                {
                    try
                    {
                        value = Double.Parse(valueInString);
                        return true;
                    }
                    catch (Exception)
                    {
                        value = 0.0;
                        return false;
                    }
                }
                else
                {
                    value = 0.0;
                    return false;
                }
            }
            else
            {
                value = 0.0;
                return false;
            }
        }

        public bool GetValueByFieldName(string field_name, out string value)
        {
            string valueInString;
            if (GetStringValueByFieldName(field_name, out valueInString))
            {
                valueInString.Trim();
                value = valueInString;
                return true;
            }
            else
            {
                value = "";
                return false;
            }
        }
    }
}
